package com.lemon.cloud.command;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.lemon.cloud.interceptor.CommandInterceptor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

/**
 * @author lemon
 * @version 1.0
 * @description: TODO
 * @date Create by lemon on 2019-09-14 21:07
 */
@Slf4j
@Component
public class CommandRepository {
    /**
     * PreCommandInterceptor
     */
    @Getter
    private List<CommandInterceptor> globalPreCommandInterceptorList = Lists.newArrayList();

    /**
     * PostCommandInterceptor
     */
    @Getter
    private List<CommandInterceptor> globalPostCommandInterceptorList = Lists.newArrayList();

    /**
     * CommandClass-CommandInvocation
     */
    private Map<Class, CommandInvocation> commandRepositoryMap = Maps.newHashMap();

    public CommandInvocation getCommandInvocation(Class<? extends Command> commandClass) {
        CommandInvocation commandInvocation = this.commandRepositoryMap.get(commandClass);

        if (this.commandRepositoryMap.get(commandClass) == null) {
            throw new RuntimeException(commandClass + " is not registered in CommandRepository, please register first");
        }

        return commandInvocation;
    }

    /**
     * @param commandClass
     * @param commandInvocation
     * @return void
     * @description
     * @author lemon
     * @date 2019-09-15 08:55
     */
    public void registered(Class<? extends Command> commandClass, CommandInvocation commandInvocation) {
        this.commandRepositoryMap.put(commandClass, commandInvocation);
        log.info("Command invocation to register a command [{}]", commandClass);
    }

    /**
     * @param commandInterceptor
     * @return void
     * @description
     * @author lemon
     * @date 2019-09-15 08:55
     */
    public void registedPreCommandInterceptor(CommandInterceptor commandInterceptor) {
        this.globalPreCommandInterceptorList.add(commandInterceptor);
        this.order(this.globalPreCommandInterceptorList);
        log.info("Registration pre command interceptor [{}]", commandInterceptor.getClass());
    }

    /**
     * @param commandInterceptor
     * @return void
     * @description
     * @author lemon
     * @date 2019-09-15 08:55
     */
    public void registedPostCommandInterceptor(CommandInterceptor commandInterceptor) {
        this.globalPostCommandInterceptorList.add(commandInterceptor);
        this.order(this.globalPostCommandInterceptorList);
        log.info("Registration post command interceptor [{}]", commandInterceptor.getClass());
    }

    /**
     * @param globalCommandInterceptorList
     * @return void
     * @description 按照Order有小到大排序
     * @author lemon
     * @date 2019-09-15 09:45
     */
    protected <T> void order(List<T> globalCommandInterceptorList) {
        if (globalCommandInterceptorList == null || globalCommandInterceptorList.size() <= 1) {
            return;
        }

        T newInterceptor = globalCommandInterceptorList.get(globalCommandInterceptorList.size() - 1);
        Order order = newInterceptor.getClass().getDeclaredAnnotation(Order.class);

        if (order == null) {
            return;
        }

        int index = globalCommandInterceptorList.size() - 1;

        for (int i = globalCommandInterceptorList.size() - 2; i >= 0; i--) {
            int itemOrderInt = Ordered.LOWEST_PRECEDENCE;
            Order itemOrder = globalCommandInterceptorList.get(i).getClass().getDeclaredAnnotation(Order.class);

            if (itemOrder != null) {
                itemOrderInt = itemOrder.value();
            }
            if (itemOrderInt > order.value()) {
                globalCommandInterceptorList.set(index, globalCommandInterceptorList.get(i));
                index = i;
            } else {
                break;
            }
        }

        if (index < globalCommandInterceptorList.size() - 1) {
            globalCommandInterceptorList.set(index, newInterceptor);
        }
    }
}
